package os;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import vm.MainWindowController;

/**
 *
 * @author Paulius Dambrauskas
 */
public class OS {
    public static final String[] STATES = {
        "Vykdomas",                 //0
        "Blokuotas",                //1
        "Pasiruošęs",               //2
        "Sustabdytas blokuotas",    //3
        "Sustabdytas pasiruošęs"    //4
    };
    public static int prevProcess = 0;
    public static int currentProcess = 0; //Einamasis procesas
    public static Map<Integer, Process> processes = new HashMap<>(); //Mapas, jei netyčia peršoktų nr, ir naujas procas negautų seno id
    public static Map<String, OSResource> resources = new HashMap<>();
    public static ArrayList<Integer> readyList = new ArrayList<>();
    public static int processCount = 0; //naudojamas procesų unikaliem nr
    private static int resourceCount = 0; //naudojamas resursų unikaliem nr
    private static int hiestPriority = 100;
    public static MainWindowController window;
    public OS(MainWindowController controller){
        window = controller;
        createProc(-1, 0, hiestPriority, StartStop.class);
       // processes.get(0).setStep(1);
        //processes.get(0).runProcess();
        readyList.remove(readyList.indexOf(currentProcess));
    }
    
    /***************************************************************************
     * ==========================PROCESŲ PRIMITYVAI============================*
     **************************************************************************/    
    
    /**
     * Proceso kūrimo primityvas
     * @param proc 
     */
    public static void createProc(int fatherId, int startState, int priority, Class proc) {
        try{
            
            Process newProc = (Process)proc.newInstance();
            newProc.create();
            newProc.setPriority(priority);
            newProc.setState(startState);
            
            newProc.setSelfId(processCount);
            newProc.fatherId = fatherId;
            hiestPriority--;
            processes.put(processCount, newProc);
            //System.out.println(processCount+" "+proc.getName());
            readyList.add(processCount);
            if(fatherId > -1)
                processes.get(fatherId).children.add(processCount);
            processCount++;
            System.out.println("Sukurtas procas "+proc.getName());
        } catch (InstantiationException | IllegalAccessException e){
            System.out.println("Nepavyko sukurt proceso, blogas klasės vardas ar pan.");
        }
        window.initProcessTable();
    }
    /**
     * Proceso stabdymo primityvas
     * @param procId 
     */
    public static void stopProc(int procId){
        Process proc =  processes.get(procId);
        int currentState = proc.getState();
        switch(currentState){
            case 1 : {
                proc.setState(3);
                break;
            }
            case 2 : {
                proc.setState(4);
                break;
            }
        }
        window.initProcessTable();
    }
    /**
     * Proceso aktyvavimo primityvas
     * @param procId 
     */
    public static void activateProc(int procId){
        Process proc =  processes.get(procId);
        int currentState = proc.getState();
        switch(currentState){
            case 3 : {
                proc.setState(1);
                break;
            }
            case 4 : {
                proc.setState(2);
                break;
            }
        }
        window.initProcessTable();
    }
    /**
     * Proceso naikinimo deskriptorius
     * @param procId 
     */
    public static void destroyProc(int procId){
        Process proc =  processes.get(procId);
        System.out.println("Naikinti: "+proc.getClass().getName());
        //Atia vaikams
        for(int child : proc.children){
            try{
                destroyProc(child);
            }catch(Exception e){}
        }
        //Atia resursasma
        for(int i = 0; i < proc.createdResources.size(); i++){
            try{
                destroyRes(proc.createdResources.get(i));
            }catch(Exception e){}
        }
        processes.get(proc.fatherId).children.remove(processes.get(proc.fatherId).children.indexOf(procId)); //Pašalinam iš tėvo vaikų
        processes.remove(procId);
        try{
            readyList.remove(readyList.indexOf(procId)); //Lauk iš pasiruošusių listo
        } catch (Exception e){}
        window.initProcessTable();
    }
    
    /***************************************************************************
     * ==========================RESURSŲ PRIMITYVAI============================*
     **************************************************************************/  
    
    /**
     * Resurso kūrimo primityvas
     * @param fatherId
     * @param res 
     */
    public static void createRes(int fatherId, String name){
            OSResource reso = new OSResource();
            reso.setFatherId(fatherId);
            reso.setName(name);
            resources.put(name, reso);
            processes.get(fatherId).createdResources.add(name);
            resourceCount++;
            //System.out.println("Create: "+name);
            window.initResourceTable();//kalė, pati neatsinaujina
            //distributor(); nekviesiu, nes resursa tuscia sukuria
    }
    public static void createResElem(ResourceElement element){
        resources.get(element.resourceName).elementList.add(element);
        resources.get(element.resourceName).setCountValue();
        System.out.println(processes.get(currentProcess).getClass().getName()+"(?) sukūrė/atlaisvino "+ element.resourceName+"("+element.value.toString()+")");
        window.initResourceTable();
    }
    /**
     * Resurso prašymo primityvas
     * @param resId
     * @param procId 
     */
    public static void askRes(String resName, int procId){
        processes.get(procId).setState(1);
        resources.get(resName).waitingList.add(procId);
        System.out.println(processes.get(procId).getClass().getName()+" prašo "+ resName + " resurso");
        window.initResourceTable();
        distributor();
        
    }
    public static void freeRes(ResourceElement element){
        resources.get(element.resourceName).elementList.add(element);
        readyList.add(currentProcess);//?
        //processes.get(currentProc);
        resources.get(element.resourceName).setCountValue();
        System.out.println(processes.get(currentProcess).getClass().getName()+" sukūrė/atlaisvino "+ element.resourceName+"("+element.value.toString()+")");
        window.initResourceTable();
        distributor();
    }
    /**
     * Resurso naikinimo primityvas
     * @param resId 
     */
    public static void destroyRes(String resName){
        OSResource res = resources.get(resName);
        System.out.println(processes.get(currentProcess).getClass().getName()+" sunaikino "+ res.getName());
        processes.get(res.getFatherId()).createdResources.remove(processes.get(res.getFatherId()).createdResources.indexOf(resName)); // Šalinam iš sukurtų listo
        //Atblokinam laukiančius
        for(int proc : res.waitingList){
            try{
                processes.get(proc).setState(2); //Atblokuotas - tai 2?
            } catch (Exception e){}
        }
        
        resources.remove(resName);
        window.initResourceTable();
    }
    /**
     * Ar kaip ten angliškai panuotojas :) 
     */
    public static void sheduler(){
        
        try{
            processes.get(currentProcess).setState(1);              //užblokinam dabartini
            /*if(processes.get(0).children.indexOf(processes.get(currentProcess).selfId) == -1){
                System.out.println("Mazinam "+processes.get(currentProcess).getClass().getName()+
                        " prioriteta ("+processes.get(currentProcess).getPriority()+" -> "+(processes.get(currentProcess).getPriority()-1)+")");
                processes.get(currentProcess).setPriority(processes.get(currentProcess).getPriority()-1);
            }*/
            prevProcess = currentProcess;
            currentProcess = hiestProcess(readyList);               //parenkam nauja
            //System.out.println(currentProcess+" "+readyList.size());
            Process currentP = processes.get(currentProcess);
            System.out.println("Vykdymas->"+currentP.getClass().getName());
            //currentP.setStep(currentP.step+1);
            readyList.remove(readyList.indexOf(currentProcess));    //ismetam nauja is pasiruosusiu
            currentP.setState(0);              //liepiam nauja vykdyt
            
        } catch (Exception e){
           // distributor(); // gal resursu atsirado ;)
            System.out.println("Nebėr laukiančių");
            //createRes(0, "OS pabaiga");
            freeRes(new ResourceElement("OS Pabaiga", "DIE"));
        }
    }
   
    /**
     * Paskirstytojas
     */
    public static void distributor(){
        for (Map.Entry pairs : resources.entrySet()) {
            OSResource resource = (OSResource) pairs.getValue();
           // System.out.println(resource.waitingList.size()+" "+resource.elementList.size());
            while(resource.waitingList.size() > 0 && resource.elementList.size() > 0){
                
                int pId = hiestProcess(resource.waitingList);
                Process proc = processes.get(pId);
                ResourceElement resEl = resource.elementList.get(resource.elementList.size()-1);
                if(resEl.getTarget() == -1){
                    proc.givenResources.add(resEl); //duodam
                    //proc.setResCountValue(1);
                    System.out.println(proc.getClass().getName()+" gavo "+resource.elementList.get(resource.elementList.size()-1).resourceName+"("+
                            resource.elementList.get(resource.elementList.size()-1).value+")");

                    resource.elementList.remove(resource.elementList.size()-1); //ismetam
                    resource.waitingList.remove(resource.waitingList.indexOf(pId)); //? kad daugiau nebeprašytų
                    //perpie6iam lentelej
                    resource.setCountValue();
                    //gavo resursą, tai bus pasiruošęs
                    proc.setState(2);
                    readyList.add(pId);
                    //sheduler();
                } else {
                    if(resource.waitingList.indexOf(resEl.getTarget()) != -1){
                        //System.out.println("Su targetu");
                        proc = processes.get(resEl.getTarget());
                        proc.givenResources.add(resEl);
                        System.out.println(proc.getClass().getName()+" gavo "+resource.elementList.get(resource.elementList.size()-1).resourceName+"("+
                            resource.elementList.get(resource.elementList.size()-1).value+")");

                        resource.elementList.remove(resource.elementList.size()-1); //ismetam
                        resource.waitingList.remove(resource.waitingList.indexOf(pId)); //? kad daugiau nebeprašytų
                        //perpie6iam lentelej
                        resource.setCountValue();
                        //gavo resursą, tai bus pasiruošęs
                        proc.setState(2);
                        readyList.add(resEl.getTarget());
                        //sheduler();
                    } else {
                        break;
                    }
                }
            }
        }
        sheduler();
    }
    
    public static int hiestProcess(ArrayList<Integer> list){
        int processId = 0;
        int hiestPriority2 = -999999999;
        for(int process : list){
            if(processes.get(process).getPriority() > hiestPriority2){
                hiestPriority2 = processes.get(process).getPriority();
                processId = process;
            }
        }
        return processId;
    }

}
